import os
from typing import List, Union, Optional, Dict, Tuple

import smtplib
from email.utils import formataddr
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

from .config import CONF


def get_attach(fpath, fname=''):
    if not fname:
        fname = os.path.split(fpath)[1]
    att = MIMEText(open(fpath, 'rb').read(), 'base64', 'utf-8')
    att["Content-Type"] = 'application/octet-stream'
    att["Content-Disposition"] = 'attachment; filename="{}"'.format(fname)
    return att


def send_email(
    Destination: Union[str, List[str]],
    Subject: str,
    Data: str,
    DataType: str,  # text/html
    Attachments: Union[List[str], Dict[str, str]] = [],  # Dict[fpath, fname]
    FromRealName: Optional[str] = None,
    FromEmailAddress: Optional[str] = None,
    is_ssl: Optional[bool] = None,
    host: Optional[str] = None,
    port: Optional[int] = None,
    username: Optional[str] = None,
    password: Optional[str] = None,
    defaultConf: Optional[Dict[str, str]] = CONF['smtp']
) -> Tuple[int, str]:
    """
    SMTP发送邮件

    Args:
        Destination (Union[str, List[str]]): 收件人(列表)
        Subject (str): 邮件主题
        Data (str): 正文, text/html
        DataType (str): 'text'/'html'
        Attachments (Union[List[str], Dict[str, str]]): List[fpath]/Dict[fpath, fname]
        FromRealName (Optional[str], optional): 发件人名称. Defaults to None.
        FromEmailAddress (Optional[str], optional): 发件人地址. Defaults to None.
        is_ssl (Optional[bool], optional): SMTP是否SSL. Defaults to None.
        host (Optional[str], optional): SMTP host. Defaults to None.
        port (Optional[int], optional): SMTP port. Defaults to None.
        username (Optional[str], optional): SMTP username. Defaults to None.
        password (Optional[str], optional): SMTP password. Defaults to None.
        defaultConf (Optional[Dict[str, str]], optional): 默认配置, 配置项为None时会从该字典中获取. Defaults to CONF['smtp'].

    Returns:
        Tuple[int, str]: err_code, err_info
    """
    if not isinstance(Destination, list):
        Destination = [Destination]

    if FromRealName is None:
        FromRealName = defaultConf['FromRealName']
    if FromEmailAddress is None:
        FromEmailAddress = defaultConf['FromEmailAddress']
    if is_ssl is None:
        is_ssl = defaultConf['is_ssl']
    if host is None:
        host = defaultConf['host']
    if port is None:
        port = defaultConf['port']
    if username is None:
        username = defaultConf['username']
    if password is None:
        password = defaultConf['password']

    err_code = 0
    err_info = ''

    try:
        _subtype = 'plain'
        if DataType.lower() == 'html':
            _subtype = 'html'
        if '\r\n' not in Data and '\n' in Data:
            Data = Data.replace('\n', '\r\n')
        if Attachments:
            message = MIMEMultipart()
        else:
            message = MIMEText(Data, _subtype, 'utf-8')
        message['From'] = formataddr((FromRealName, FromEmailAddress))
        message['Subject'] = Subject

        if Attachments:
            message.attach(MIMEText(Data, _subtype, 'utf-8'))
            if isinstance(Attachments, dict):
                for fpath, fname in Attachments.items():
                    message.attach(get_attach(fpath=fpath, fname=fname))
            else:
                for fpath in Attachments:
                    message.attach(get_attach(fpath=fpath))

        if is_ssl:
            smtpObj = smtplib.SMTP_SSL(host, port)
        else:
            smtpObj = smtplib.SMTP(host, port)
        smtpObj.login(username, password)
        smtpObj.sendmail(
            FromEmailAddress,
            Destination,
            message.as_string()
        )
        smtpObj.quit()
    except smtplib.SMTPException as err:
        err_code = 1
        err_info = "[smtplib.SMTPException] " + str(err)
    except Exception as err:
        err_code = 1
        err_info = "[Exception] " + str(err)
    return err_code, err_info
